package com.wmjsoft.plugins.security.jwt

import com.wmjsoft.plugins.security.user.UserDetails
import io.jsonwebtoken.Claims
import io.jsonwebtoken.Jwts
import java.util.*
import java.util.UUID

import io.jsonwebtoken.SignatureAlgorithm
import org.xml.sax.ErrorHandler
import org.xml.sax.SAXParseException

import java.util.HashMap





class JwtSigner {

    val key:String = "justAJwtSingleKey"
    val authorities:String = "authorities"
    val issuer:String = "identity"

    var duration:Long = 3600

    /**
     * 用户登录成功后生成Jwt
     * 使用Hs256算法  私匙使用用户密码
     *
     * @param ttlMillis jwt过期时间
     * @param user      登录成功的user对象
     * @return
     */
    fun createJWT( user: UserDetails): String? {
        //指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        val signatureAlgorithm = SignatureAlgorithm.HS256

        //生成JWT的时间
        val nowMillis = System.currentTimeMillis()
        val now = Date(nowMillis)

        //创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
        val claims: MutableMap<String, Any> = HashMap()
        claims["id"] = user.id!!
        claims["username"] = user.username!!
        claims["password"] = user.password!!

        //生成签名的时候使用的秘钥secret,这个方法本地封装了的，一般可以从本地配置文件中读取，切记这个秘钥不能外露哦。它就是你服务端的私钥，在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
        val key: String = key

        //生成签发人
        val subject: String = user.username!!


        //下面就是在为payload添加各种标准声明和私有声明了
        //这里其实就是new一个JwtBuilder，设置jwt的body
        val builder = Jwts.builder() //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
            .setClaims(claims) //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
            .setId(UUID.randomUUID().toString()) //iat: jwt的签发时间
            .setIssuer(issuer)
            .setIssuedAt(now) //代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
            .setSubject(subject) //设置签名使用的签名算法和签名使用的秘钥
            .signWith(signatureAlgorithm, key)
        if (duration >= 0) {
            val expMillis = nowMillis + duration
            val exp = Date(expMillis)
            //设置过期时间
            builder.setExpiration(exp)
        }
        return builder.compact()
    }


    /**
     * Token的解密
     * @param token 加密后的token
     * @param user  用户的对象
     * @return
     */
    fun parseJWT(token: String): Claims? {
        //签名秘钥，和生成的签名的秘钥一模一样
        val key: String = key

        //得到DefaultJwtParser
        return Jwts.parser() //设置签名的秘钥
            .setSigningKey(key) //设置需要解析的jwt
            .parseClaimsJws(token)
            .body
    }


    /**
     * 校验token
     * 在这里可以使用官方的校验，我这里校验的是token中携带的密码于数据库一致的话就校验通过
     * @param token
     * @param user
     * @return
     */
    fun isVerify(token: String?, user: UserDetails): Boolean {
        //签名秘钥，和生成的签名的秘钥一模一样
        val key: String? = user.password

        //得到DefaultJwtParser
        val claims = Jwts.parser() //设置签名的秘钥
            .setSigningKey(key) //设置需要解析的jwt
            .parseClaimsJws(token).body
        return claims["password"] == user.password
    }



}